/*
 * acooly.cn Inc.
 * Copyright (c) 2016 All Rights Reserved.
 * create by zhangpu
 * date:2016年4月4日
 *
 */
package com.acooly.module.openapi.client.provider.yipay.marshall;

import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.core.utils.validate.Validators;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.util.Jsons;
import com.acooly.module.openapi.client.provider.yipay.OpenAPIClientYipayProperties;
import com.acooly.module.openapi.client.provider.yipay.YipayConstants;
import com.acooly.module.openapi.client.provider.yipay.domain.*;
import com.acooly.module.openapi.client.provider.yipay.enums.YipayApiServiceType;
import com.acooly.module.openapi.client.provider.yipay.exception.YipayProcessingException;
import com.acooly.module.openapi.client.provider.yipay.utils.SignUtils;
import com.acooly.module.openapi.client.provider.yipay.utils.StringHelper;
import com.acooly.module.safety.Safes;
import com.acooly.module.safety.key.KeyLoadManager;
import com.acooly.module.safety.signature.SignTypeEnum;
import com.acooly.module.safety.support.KeyStoreInfo;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

/**
 * @author zhangpu
 */
@Slf4j
public class YipayAbstractMarshall {

    @Autowired
    protected OpenAPIClientYipayProperties openAPIClientFudianProperties;

    @Autowired
    protected KeyLoadManager keyStoreLoadManager;

    @Resource(name = "yipayMessageFactory")
    private MessageFactory messageFactory;


    protected String doMarshall(YipayRequest source) {
        // pre
        source.setPartner(getProperties().getMerchantNo());
        source.setService(YipayConstants.getServiceName(source));
        source.setOrgCode(source.getPartner());
        settingNotifyUrl(source);
        doVerifyParam(source);
        Map<String, String> signMap = getSignDataMap(source);
        String orignSignStr = StringHelper.getOrignSign(signMap);
        log.info("待签字符串：{}",orignSignStr);
        String sign = doSign(orignSignStr);
        signMap.put(YipayConstants.SIGN,sign);
        String encryptDataSrc = Jsons.toJson(signMap);
        //加密生成业务参数原文
        String encryptData=SignUtils.encode(getProperties().getAesKey(),encryptDataSrc,getProperties().getAesVi());
        //加密AESKey
        String encryptKey = SignUtils.rsaEncode(getProperties().getAesKey(),getKeyStore().getCertificate().getPublicKey());
        Map<String, String> datas = Maps.newTreeMap();
        datas.put(YipayConstants.MERCHANT_NO, getProperties().getMerchantNo());
        datas.put(YipayConstants.ENCRYPT_KEY, encryptKey);
        datas.put(YipayConstants.ENCRYPT_DATA, encryptData);
        String message = Jsons.toJson(datas);
        return message;
    }


    protected YipayResponse doUnMarshall(String message, String serviceName, boolean notify) {
        try {
            log.info("响应报文:{}", message);
            JSONObject jsonObject = JSONObject.parseObject(message, Feature.OrderedField);
            String result = jsonObject.getString(YipayConstants.RESPONSE_BODY_KEY);
            String sign = jsonObject.getString(YipayConstants.SIGN);
            String verifySignStr = getVerifySignStr(jsonObject);
            //验签
            doVerify(verifySignStr, sign);
            YipayResponse yipayResponse = null;
            if (notify) {
                yipayResponse = (YipayResponse) messageFactory.getNotify(serviceName);
            } else {
                yipayResponse = (YipayResponse) messageFactory.getResponse(serviceName);
            }
            if(Strings.isNotBlank(result)&&StringHelper.isJson(result)) {
                yipayResponse = Jsons.parse(result, yipayResponse.getClass());
            } else {
                yipayResponse = Jsons.parse(message, yipayResponse.getClass());
            }
            YipayResponseWrap yipayResponseWrap = Jsons.parse(message, YipayResponseWrap.class);
            yipayResponse.setService(serviceName);
            yipayResponse.setSuccess(yipayResponseWrap.getSuccess());
            yipayResponse.setRetCode(yipayResponseWrap.getErrorCode());
            yipayResponse.setRetMsg(yipayResponseWrap.getErrorMsg());
            return yipayResponse;
        } catch (Exception e) {
            throw new YipayProcessingException("解析报文错误:" + e.getMessage());
        }
    }

    public void settingNotifyUrl(YipayRequest apiMessage) {
        YipayApiMsg yipayApiMsg = YipayConstants.getApiMsgInfo(apiMessage);
        if (Strings.isBlank(apiMessage.getNotifyUrl()) && yipayApiMsg.service().getApiServiceType() != YipayApiServiceType.SYNC) {
            apiMessage.setNotifyUrl(YipayConstants.getCanonicalUrl(openAPIClientFudianProperties.getDomain(),
                    openAPIClientFudianProperties.getNotifyUrl()
                            + yipayApiMsg.service().code()));
        }
        if (yipayApiMsg.service().getApiServiceType() == YipayApiServiceType.REDIRECT && Strings.isBlank(apiMessage.getReturnUrl())) {
            throw new RuntimeException("跳转接口returnUrl是必须的");
        }
    }

    /**
     * 获取代签map
     *
     * @param source
     * @return
     */
    protected TreeMap<String, String> getSignDataMap(YipayRequest source) {
        TreeMap<String, String> signData = Maps.newTreeMap();
        Set<Field> fields = Reflections.getFields(source.getClass());
        String key = null;
        Object value = null;
        for (Field field : fields) {
            value = Reflections.getFieldValue(source, field.getName());
            if (value == null) {
                continue;
            }
            YipayAlias baofuAlias = field.getAnnotation(YipayAlias.class);
            if (baofuAlias == null) {
                continue;
            } else {
                key = baofuAlias.value();
            }
            if (Strings.isNotBlank((String) value)) {
                signData.put(key, (String) value);
            }
        }
        return signData;
    }

    /**
     * 获取验签字符串
     *
     * @param responseMessage
     * @return
     */
    protected String getVerifySignStr(JSONObject responseMessage) {
        Iterator<Map.Entry<String, Object>> sIterator = responseMessage.entrySet().iterator();
        StringBuffer sb=new StringBuffer();
        while(sIterator.hasNext()) {
            Map.Entry<String, Object> entry =sIterator.next();
            String key = entry.getKey();
            if(!Strings.equals(YipayConstants.SIGN,key)){
                sb.append("&").append(key).append("=").append(entry.getValue());
            }
        }
        String tobeVerify=sb.substring(1);
        return tobeVerify;
    }

    protected OpenAPIClientYipayProperties getProperties() {
        return openAPIClientFudianProperties;
    }


    protected void doVerifyParam(YipayMessage source) {
        try {
            source.doCheck();
            Validators.assertJSR303(source);
        } catch (Exception e) {
            throw new ApiClientException(e.getMessage());
        }
    }

    protected String doSign(String waitForSign) {
        return Safes.getSigner(SignTypeEnum.Cert).sign(waitForSign, getKeyStore());
    }

    protected void doVerify(String plain, String sign) {
        try {
            Safes.getSigner(SignTypeEnum.Cert).verify(plain, getKeyStore(), sign);
            log.info("验签成功");
        }catch (Exception e) {
            throw new ApiClientException("验签失败："+e.getMessage());
        }
    }

    protected KeyStoreInfo getKeyStore() {
        return keyStoreLoadManager.load(YipayConstants.PROVIDER_DEF_PRINCIPAL, YipayConstants.PROVIDER_NAME);
    }


}
